package Day_0223.Spring;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author zxc
 * @date 2023/02/23 15:20
 **/
public class Interface_Called_Only_Once {
    public static void main(String[] args) {
        /**
         * 第二大类：只调用一次的接口
         * ===》
         * 一，特点
         * 这一大类接口的特点是功能丰富，常用于用户自定义扩展。
         *
         * 二，分类
         * 第二大类中又可以分为两类：
         * 1. Aware类型的接口
         * 2. 生命周期接口
         *
         * 三，Aware类型接口
         * 1.Aware类型接口的作用，就是让我们能够拿到Spring容器中的一些资源。
         *
         * 2.调用时机需要注意：
         * ===》所有的Aware方法都是在初始化阶段之前调用的！
         *
         * 3.Aware接口分类
         * 1）Aware Group1
         * （1）BeanNameAware
         * （2）BeanClassLoaderAware
         * （3）BeanFactoryAware
         * 2）Aware Group2
         * （1）EnvironmentAware
         * （2）EmbeddedValueResolverAware
         *  实现该接口能够获取Spring EL解析器，用户的自定义注解需要支持spel表达式的时候可以使用，非常方便。
         * 3）ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisherAware\MessageSourceAware)
         * ===》
         * （1）其返回值实质上都是当前的ApplicationContext对象，
         *  因为ApplicationContext是一个复合接口，如下：
         *  ===》
         *  public interface ApplicationContext extends EnvironmentCapable,ListableBeanFactory,
         *  HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}
         * （2）思考 ：ApplicationContext是复合接口，其所具备那些BeanFactory所不具备的功能
         *  这里涉及到另一道面试题，ApplicationContext和BeanFactory的区别，
         *  可以从ApplicationContext继承的这几个接口入手，除去BeanFactory相关的两个接口就是ApplicationContext独有的功能。
         *  ===》
         *  即，除了ApplicationContext继承的和BeanFactory相关的接口之外，其他ApplicationContext所继承的接口都是其特有功能;
         *
         * 4）Aware调用时机
         * （1）可以看到并不是所有的Aware接口都使用同样的方式调用。
         *  Bean××Aware都是在代码中直接调用的，
         *  而ApplicationContext相关的Aware都是通过BeanPostProcessor#postProcessBeforeInitialization()实现的。
         * （2）至于Spring为什么这么实现，应该没什么特殊的考量。也许和Spring的版本升级有关。
         *  基于对修改关闭，对扩展开放的原则，Spring对一些新的Aware采用了扩展的方式添加。
         * （3）BeanPostProcessor的调用时机也能在这里体现，包围住invokeInitMethods方法，也就说明了在初始化阶段的前后执行。
         * （4）关于Aware接口的执行顺序，其实只需要记住第一组在第二组执行之前就行了。
         *  每组中各个Aware方法的调用顺序其实没有必要记，有需要的时候点进源码一看便知。
         *
         * ===》
         * 总结 ：
         * （1）Aware第一类接口（类似于Bean...Aware），是直接在代码中进行调用;
         *  同时，该类型接口的调用时机是在 Aware第二类接口之前进行调用;
         * （2）Aware第二类接口（即，与ApplicationContext相关的接口），
         *  是通过PostProcessBeforeInitialization方法来进行调用的;
         *
         * 四，生命周期接口
         * ===》
         * 1.至于剩下的两个生命周期接口就很简单了，实例化和属性赋值都是Spring帮助我们做的，能够自己实现的有初始化和销毁两个生命周期阶段。
         * 2.即，由于Bean实例化和属性赋值都是Spring已经实现的，只有Bean初始化和销毁是需要手动实现的;
         *
         * 3.InitializingBean接口
         * 1）InitializingBean顾名思义，是初始化Bean相关的接口。
         * 2）接口定义：
         * ===》
         * public interface InitializingBean {
         * void afterPropertiesSet() throws Exception;
         * }
         * （1）看方法名，是在读完Properties文件，之后执行的方法。
         * （2）afterPropertiesSet()方法是在初始化过程中被调用的。
         *
         * 3）InitializingBean 对应生命周期的初始化阶段，
         * 在上面源码的invokeInitMethods(beanName,wrappedBean, mbd);方法中调用。
         * 4）因为Aware方法都是执行在初始化方法之前，
         * 所以可以在初始化方法中放心大胆的使用Aware接口获取的资源，这也是我们自定义扩展Spring的常用方式。
         * 5）三种实现指定初始化的方法
         * （1）使用@PostConstruct注解，该注解作用于void方法上
         * （2）在配置文件中配置init-method方法
         * （3）将类实现InitializingBean接口
         *
         * 4.DisposableBean接口
         * （1）DisposableBean 类似于InitializingBean，对应生命周期的销毁阶段，
         * （2）以ConfigurableApplicationContext#close()方法作为入口，
         * ===》
         * 实现是，通过循环获取所有实现了DisposableBean接口的Bean，然后调用其destroy()方法。
         * （3）DisposableBean接口定义 ：
         *  public interface DisposableBean {
         *  void destroy() throws Exception;
         *  }
         *  ===》
         *  总结 ：
         *  （1）也就是说，在对象销毁的时候，会去调用DisposableBean的destroy方法。
         *  （2）在进入到销毁过程时，先去调用一下DisposableBean的destroy方法，
         *  （3）然后，执行 destroy-method声明的方法（用来销毁Bean中的各项数据）。
         *
         */
        AbstractApplicationContext applicationContext = new
                ClassPathXmlApplicationContext("classpath:application-usertag.xml");
        System.out.println("init-success");
        applicationContext.registerShutdownHook();
    }
}
class IndexBean implements InitializingBean, DisposableBean {
    public void destroy() throws Exception {
        System.out.println("destroy");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("init-afterPropertiesSet()");
    }
    public void test(){
        System.out.println("init-test()");
    }
}
